package io.gitee.yxsnake.mybatis.plus.interceptor;

import com.google.common.base.Throwables;
import io.gitee.yxsnake.embark.web.core.constant.StringPool;
import io.gitee.yxsnake.embark.web.core.exception.DataStorageException;
import io.gitee.yxsnake.embark.web.core.secret.sm.SM3Utils;
import io.gitee.yxsnake.embark.web.core.secret.sm.SM4Utils;
import io.gitee.yxsnake.mybatis.plus.annotation.SensitiveData;
import io.gitee.yxsnake.mybatis.plus.annotation.SensitiveField;
import io.gitee.yxsnake.mybatis.plus.configuration.properties.MybatisPlusExtProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.*;
import org.dromara.hutool.core.collection.CollUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.springframework.core.annotation.AnnotationUtils;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.sql.Statement;
import java.util.*;

/**
 * @author snake
 * @description
 * @since 2024/10/21 0:12
 */
@Slf4j
@Intercepts({
        @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})
@RequiredArgsConstructor
public class DataDecryptInterceptor implements Interceptor {

    private final MybatisPlusExtProperties mybatisPlusExtProperties;

    private final String sm4Key;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        //取出查询的结果
        Object resultObject = invocation.proceed();
        if (Objects.isNull(resultObject)) {
            return null;
        }
        //基于selectList
        if (resultObject instanceof ArrayList) {
            ArrayList resultList = (ArrayList) resultObject;
            if (CollUtil.isNotEmpty(resultList) && needToDecrypt(resultList.stream().findFirst().get())) {
                for (Object result : resultList) {
                    //逐一解密
                    this.decrypt(result);
                }
            }
            //基于selectOne
        }else {
            if (needToDecrypt(resultObject)) {
                this.decrypt(resultObject);
            }
        }
        return resultObject;
    }

    private void decrypt(Object result) throws IllegalAccessException {
        Class<?> resultClass = result.getClass();
        Field[] declaredFields = resultClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            //去除所有被EncryptDecryptFiled注解的字段
            SensitiveField sensitiveFiled = declaredField.getAnnotation(SensitiveField.class);
            if (!Objects.isNull(sensitiveFiled)) {
                //将此对象的 accessible 标志设置为指示的布尔值。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
                declaredField.setAccessible(true);
                //这里的result就相当于是字段的访问器
                Object object = declaredField.get(result);
                //只支持String解密
                if (object instanceof String) {
                    String value = (String) object;
                    String text = decrypt(value);
                    //对注解在这段进行逐一解密
                    declaredField.set(result, text);
                }
            }
        }
    }

    /**
     * 对单个结果集判空的一个方法
     * @param object
     * @return
     */
    private boolean needToDecrypt(Object object) {
        Class<?> objectClass = object.getClass();
        SensitiveData dbSensitiveEntity = AnnotationUtils.findAnnotation(objectClass, SensitiveData.class);
        return Objects.nonNull(dbSensitiveEntity);
    }

    /**
     * 将此过滤器加入到过滤器链当中
     * @param target
     * @return
     */
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }

    /**
     * 解密
     * @param content
     * @return
     */
    private String decrypt(String content){
        String decryptContent = null;
        if(StrUtil.isBlank(content)){
            return null;
        }
        //解密数据
        String decrypt = null;
        try {
            decrypt = SM4Utils.decrypt(sm4Key, content);
        } catch (Exception e) {
            log.error("decrypt sm4 exception:{}", Throwables.getStackTraceAsString(e));
            throw new DataStorageException(500,"解密数据失败");
        }
        List<String> data = Arrays.asList(decrypt.split(StringPool.DOT));
        if(data.size() != 2){
            log.warn("加密数据未拆分出密文和签名值");
            throw new DataStorageException(500,"数据完整性遭到破坏");
        }
        String encryptText = decryptContent = data.get(0);
        String signText = data.get(1);
        //对密文 进行签名 并验签
        String sign = null;
        try {
            sign = SM3Utils.encryptText(encryptText);
        } catch (UnsupportedEncodingException e) {
            log.error("sign sm3 exception:{}", Throwables.getStackTraceAsString(e));
            throw new DataStorageException(500,"数据签名失败");
        }
        if(!sign.equals(signText)){
            throw new DataStorageException(500,"数据已被篡改");
        }
        return decryptContent;
    }




}
